package com;

import com.handler.*;
import com.message.ChatGroupCreateMessage;
import com.message.factory.MessageDeSerializerFactory;
import com.message.resp.ChatGroupCreateResp;
import com.protocol.SharedMessageCodec;
import com.session.ChatServerContext;
import com.session.ChatServerContextFactory;
import com.session.GroupChatServerFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.nio.channels.ServerSocketChannel;
import java.util.Map;


@Slf4j
public class ChatServer {


    public static void main(String[] args) {


        LoggingHandler LOGGING_HANDLER = new LoggingHandler();

        SharedMessageCodec SHARED_MESSAGE_CODEC = new SharedMessageCodec(new MessageDeSerializerFactory());

        ChatDispatchHandler CHAT_DISPATCH_HANDLER = new ChatDispatchHandler();

        ChatLoginHandler CHAT_LOGIN_HANDLER = new ChatLoginHandler();

        CreateGroupChatHandler CREATE_GROUP_CHAT_HANDLER = new CreateGroupChatHandler();

        GroupChatDispatchHandler GROUP_CHAT_DISPATCH_HANDLER = new GroupChatDispatchHandler();

        ExitGroupHandler EXIT_GROUP_HANDLER = new ExitGroupHandler();

        ChatLoginOutHandler CHAT_LOGIN_OUT_HANDLER = new ChatLoginOutHandler();

        int IDLE_TIME_OUT_LIMIT = 5;

        new ServerBootstrap()
                .group(new NioEventLoopGroup(), new NioEventLoopGroup(2))
                // 服务端调用 option() 是给 ServerSocketChannel 配置参数
//                设置 tcp 连接中的全连接等待队列的大小
//                java.nio.channels.ServerSocketChannel.bind(java.net.SocketAddress, int)
//                io.netty.channel.socket.nio.NioServerSocketChannel.doBind
//                io.netty.util.NetUtil.SOMAXCONN
//                注意：实际使用时，该值会与 /proc/sys/net/core/somaxconn 中的值比较，然后实际应用两者的最小值
                //TCP三次握手链接成功队列

                .option(ChannelOption.SO_BACKLOG, 40930)
                //禁用Nagle算法，数据立马发送出去，不需要等待将多个较小的包组合成较大的包进行发送
                .childOption(ChannelOption.TCP_NODELAY, true)

//                .childOption()// 服务端调用 childOption() 是给 SocketChannel 配置参数
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(
                                // 判断是不是读、写时间过长
                                new IdleStateHandler(IDLE_TIME_OUT_LIMIT, 0, 0),
//                                 5s 内如果没有读到 channel 的数据，会触发一个 IdleStateEvent 事件
//                                ChannelDuplexHandler 可以同时作为一个入站和出站处理器
//                                因为 IdleStateHandler 针对的是读写事件都有
                                new ChannelDuplexHandler() {
                                    /**
                                     * 触发 用户自定义事件时调用，这里是 IdleStateEvent 触发
                                     * @param ctx
                                     * @param evt
                                     * @throws Exception
                                     */
                                    @Override
                                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {


                                        /**
                                         * 追踪源码，查找 byteBuf 的 池化、是使用直接内存还是使用堆内存
                                         * 进入 ctx.alloc()
                                         *  ByteBufAllocator alloc()
                                         * 进入  ByteBufAllocator
                                         * ByteBufAllocator DEFAULT = ByteBufUtil.DEFAULT_ALLOCATOR;
                                         * 继续追踪
                                         * alloc = UnpooledByteBufAllocator.DEFAULT;
                                         *
                                         * PlatformDependent.directBufferPreferred()
                                         *
                                         *  DIRECT_BUFFER_PREFERRED = CLEANER != NOOP
                                         *                                   && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false);
                                         * -Dio.netty.noPreferDirect=true
                                         * -Dio.netty.allocator.type=unpooled 两个都只能控制  ctx.alloc() 分配的 byteBuf
                                         * 如果是netty对于网络读写所自动分配的 byteBuf ，则会强制使用 直接内存，因为效率高
                                         */
                                        ctx.alloc().buffer();
                                        if (evt instanceof IdleStateEvent) {
                                            IdleStateEvent stateEvent = (IdleStateEvent) evt;
                                            if (stateEvent.state() == IdleState.READER_IDLE) {
                                                String userName = ChatServerContextFactory.getInstance().getUserNameByChannel(ctx.channel());
                                                log.debug("超过{}秒没有读取到数据，空闲读事件超时触发,{}即将断开连接", IDLE_TIME_OUT_LIMIT, userName);
                                                ctx.channel().close();
                                            }
                                        }

                                        super.userEventTriggered(ctx, evt);
                                    }
                                },
                                LOGGING_HANDLER,
                                new LengthFieldBasedFrameDecoder(1024, 11, 4),
                                SHARED_MESSAGE_CODEC,
                                CHAT_LOGIN_HANDLER,
                                CHAT_DISPATCH_HANDLER,
                                CREATE_GROUP_CHAT_HANDLER,
                                GROUP_CHAT_DISPATCH_HANDLER,
                                EXIT_GROUP_HANDLER,
                                CHAT_LOGIN_OUT_HANDLER
                        );
                    }
                }).bind(8953);
    }


}
